#ifndef min
#define min(A,B) (((A)>(B)) ? (B) : (A))
#endif /* ~min */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <openssl/sha.h>
#include <sstream>
#include <iostream>
#include<pthread.h>
#include<math.h>
#include <queue>
#include <list>
#include <map>
#include <bitset>
#include <time.h>
#include <dirent.h>
#include <cstdlib>
#include <openssl/md5.h> 

#define MAXFILEBUF 8192

using namespace std;

////==============All the Global Variables declared below========/////////
extern struct timeval tv;
extern bool resetOptionDetected;			// flag for reset option detected in command line parsing. declared in sv_node.cc
extern char * configFileName;					// file name of config file passed from command line. declared in sv_node.cc
extern uint16_t port;
extern int autoShutDown;
extern int msgLifeTime;
extern int ttl;
extern int minNeighbour;
extern int keepAliveTimeOut;
extern int initNeighbour;
extern float neighbourStoreProb;
extern float cacheProb;
extern int cacheSize;
extern int cacheSizeInBytes;
extern int remainingCacheSizeInBytes;
extern char* logFileName;
extern float storeProb;
extern char homeDir[512];
extern int noCheck;
extern int joinTimeOut;
extern int getMsgLifeTime;
extern uint32_t location;
extern int retry;
extern FILE* logFilePtr;
extern char statusFileName[512];
extern char storeFileName[512];
extern uint32_t statusInputTTL;
extern uint32_t storeInputTTL;
extern bool logAlreadyOpen;
extern bool allowGet;
extern sigset_t set;
extern struct sigaction action;

extern bool portFlag;
extern bool retryFlag;
extern bool locFlag;
extern bool homedirFlag;
extern bool logFileFlag;
extern bool ttlFlag;
extern bool msgLifeTimeFlag;
extern bool minNeighbourFlag;
extern bool autoshutFlag;
extern bool getMsgLifeTimeFlag;
extern bool joinTimeOutFlag;
extern bool storeProbFlag;
extern bool keepAliveTimeoutFlag;
extern bool cacheProbFlag;
extern bool cacheSizeFlag;
extern bool noCheckFlag;
extern bool neighborStoreProbFlag;
extern bool initNeighbourFlag;
extern char myHostName[512];
extern bool iAmBeacon;
extern unsigned char buf[SHA_DIGEST_LENGTH];
extern char* nodeInstanceId;
extern FILE* initNeighbourListFP;
extern  FILE *namFilePtr;
extern bool broadCastSent; 
extern bool fileOpenSucces;
extern volatile bool joinTimeOutBool;
extern char errorCode;

extern bool checkFlagReceived;
extern bool softRestartFlag;

extern int joinTimeOutSockFd;
 class hostPort
{
	public:
	char* hostName;
	char* portNum;
	
	hostPort(char * s,char* t)
	{
		hostName = (char*)malloc(strlen(s));
		memset(hostName,'\0',strlen(hostName));
		//printf("\n--------------------------\n");
		strncpy(hostName,s,strlen(s));
		//hostName=s;
		portNum= (char*)malloc(strlen(t));
		strncpy(portNum,t,strlen(t));
		//printf("Key:%s and strlen(key)=%d\n",hostName,strlen(hostName));
		//printf("Value:  %s\n",portNum);
		
	}
	hostPort()
	{
		hostName=NULL;
		portNum=NULL;
	}
};






extern list<hostPort> beaconList;
extern list<hostPort> nonBeaconList;
extern map<string,int> connectedNodeList;
extern multimap<int,hostPort> joinDistList;
extern map<int,int> sockFDToQIndexMap;
extern map<int,int> keepAliveTimers;


extern time_t inittime;
extern hostPort myHostPortInfo;
extern int listenqueue;

//============All Function Prototype declared below==============////
extern void initParse();
extern unsigned char *GetUOID(char *node_inst_id, char *obj_type, unsigned char *uoid_buf,unsigned int uoid_buf_sz);
extern void printUOID(unsigned char *uoid);
extern int findBitIndex();
extern void initiateUserShutDownSequence();
extern char * convertCase(char * temp);
extern void handlerForCntrlC(int sig);
extern void writeToNAMFile(int statusInputTTL);
extern void processStoreCmd();
extern void calcBitVector(unsigned char* storeFileBitVector);
extern void	generateRandChar(char *c);
extern void processGet();
extern void msgLifetimer();
//=======pThread variables needed for beacon accept and connect========//

extern void * beaconConnect(void * t);
extern void * beaconAccept(void * t);
extern void* nonBeaconAccept(void* t);
extern void* nonBeaconJoinConnect(void *t);
extern void* joinTimeOutThread(void *t);
extern void * beaconRead(void * t);
extern void * beaconWrite(void * t);
extern void* eventDispatcher(void *t);
extern void* keepAliveTimeOutThread(void* t);
extern void* keepAliveMsg(void* t);
extern void softRestart();
extern void *get_in_addr(struct sockaddr *sa);
extern void * nonBeaconConnect(void * t);
extern void * nonBeaconAccept(void * t);
extern char * removeTrailingWhiteSpace(char * temp);
extern void remLeadingSpaces(char *string);
extern void* userTerminal(void*);
extern void* checkTimeOutThread(void* t);
extern void handleAutoShutdown(int sig);
extern void* msgLifeTimeTimer(void*);
extern void processDelete();
extern pthread_mutex_t cntrlCFlagMutex;
extern pthread_t *connectingThread;
extern pthread_t eventDispatcherThreadId;
extern pthread_t acceptingThread;
extern pthread_mutex_t printMutex;
extern pthread_t nonBeaconJoinConnectThread;
extern pthread_t nonBeaconJoinTimeOut;
extern pthread_mutex_t joinTimeOutBoolMutex;
extern pthread_mutex_t connectedNodeListMutex;
extern pthread_mutex_t wQBitsetMutex;
extern pthread_mutex_t sockIndexMapMutex;
extern pthread_mutex_t eventQueueMutex;
extern pthread_mutex_t logFileMutex;
extern pthread_mutex_t UOIDMsgLifeTimeMapMutex;

extern pthread_cond_t eventQueueCV;
extern pthread_mutex_t wrtieQMutex[512];
extern pthread_cond_t wrtieQCV[512];
extern pthread_t keepAlive;
extern pthread_t keepAliveTimeout;
extern pthread_mutex_t keepAliveMapMutex;
extern pthread_t userTermialThread;
extern pthread_t msgLifeTimer;
extern pthread_mutex_t checkTimeoutMutex;
extern pthread_cond_t checkTimeoutCV;


extern pthread_t checkTimeOut;


extern pthread_mutex_t readWriteMutex;
extern int acceptCounter;
extern pthread_t readBeacon[512];
extern pthread_t writeBeacon[512];



class msgHeaderClass
{
	public:
	unsigned char messageType;
	unsigned char UOID[20];
	unsigned char ttlInHeader;
	unsigned char reserved;
	uint32_t msgDataLength;
	msgHeaderClass()
	{
		messageType=0;
		memset(UOID,'\0',20);
		ttlInHeader=0;
		//memset(&reserved,'\0',1);
		reserved='\0';
		msgDataLength=0;
	}
};

class helloMsgDataClass
{
	public:
	uint16_t HostPort;
	char Hostname[512];
	helloMsgDataClass()
	{
		HostPort=-1;
		memset(Hostname,'\0',512);
	}

};

class joinReqMsgDataClass
{
	public:
	unsigned long HostLocation;
	uint16_t HostPort;
	char Hostname[512];
	joinReqMsgDataClass()
	{
		
		HostLocation=0;
		HostPort=-1;
		memset(&Hostname,'\0',512);
	}	

};

class joinRespMsgDataClass
{
	public:
	unsigned char UOID[20];
	uint32_t distance;
	uint16_t HostPort;
	char Hostname[512];
	joinRespMsgDataClass()
	{
		
		memset(UOID,'\0',20);
		distance=0;
		HostPort=-1;
		memset(&Hostname,'\0',512);
	}	

};

class checkRespDataClass
{
	public:
	unsigned char UOID[20];
	
	checkRespDataClass()
	{
		
		memset(UOID,'\0',20);
	
	}	

};


class notifyMsgClass
{
	public:
	char errorCode;
	notifyMsgClass(char t)
	{
		//memcpy(&errorCode,&t,1);
		errorCode=t;
	}
};

class queueItem
{
	public:
	int sockfd;
	msgHeaderClass msgHeader;
	char dataBuf[MAXFILEBUF];
	queueItem()
	{
		sockfd=-1;
		memset(&msgHeader,0,sizeof(msgHeaderClass));
		memset(dataBuf,'\0',MAXFILEBUF);
		
	}
	

};


extern list<queueItem> eventQueue;
extern list<queueItem> wrtieThreadQueue[512];
extern bitset<512> wQueueIndexBitMap;

class msgLifeTimeClass
{
	public:
	int sockOfMsgReciept;
	int msgLifeTimeValue;
	msgLifeTimeClass(int a,int b)
	{
		sockOfMsgReciept=a;
		msgLifeTimeValue=b;
	}
};

extern map<string,msgLifeTimeClass> UOIDMsgLifeTimeMap;
extern bool getFileNameSpcified;
extern bool cntrlCFlag;

extern char userInput[512];
extern bool terminate;

class statusReqMsg
{
	public:
	char statusType;
	statusReqMsg(char t)
	{
		statusType=t;
	}
};

class statusRespClass
{
	public:
	unsigned char UOID[20];
	uint16_t HostInfoLength;
	uint16_t HostPort;
	char Hostname[512];
	uint32_t RecordLength[512];
	char* Data;
	statusRespClass()
	{
		memset(UOID,'\0',20);
		HostInfoLength=-1;
		HostPort=-1;
		memset(&Hostname[0],'\0',512);
		for(int i=0;i<512;i++)
		{
			 RecordLength[i]=-1;
			 Data=NULL;
		}
		Data=NULL;
	}

};

extern map<int,list<int> > statusRespMap;





//=============================================================================================
//==========================Final Project Part 2 additions=====================================
//=============================================================================================

extern list<string> storeKeywordsList;

class bitVectorIndexItem
{
	public:
		char indexBitVector[257];
		int numFileName;

		bitVectorIndexItem()
		{
			memset(&indexBitVector[0],0x00,257);
			numFileName=-1;
		}
};

extern list<bitVectorIndexItem> indexBitVectoreList;
extern multimap<string,int> indexSHA1Map;
extern map<int,string> numToFileIdMap;

class indexFileMetaInfo
{
	public:
	char originalFileName[256];
	int numFileName;
	
	unsigned char fileID[21];
	unsigned int filesize;
	char filePassword[21];
	bool isInCache;
	unsigned char fileNonceValue[41];

	indexFileMetaInfo()
	{
		memset(&originalFileName[0],'\0',256);
		numFileName = 0;
	
		memset(&fileID[0],'\0',21);
		filesize = 0;
		memset(&filePassword[0],'\0',21);
		isInCache = false;
		memset(&fileNonceValue[0],'\0',41);

	}

	
};
extern list<indexFileMetaInfo> indexFileMetaInfoList;

extern pthread_mutex_t indexMutex;
extern pthread_mutex_t tempfileindexlock;
extern pthread_mutex_t statusFileWriteLock;
extern int numFileNameCounter;
extern int tempFileIndex;
extern int lastStatusType;

class searchResponseInfo
{
	public:
		unsigned char fileID[21];
		char originalFileName[256];
		bool isOrigin;
		char fileMetaBuffer[8192];

	searchResponseInfo()
	{
		memset(fileID,'\0',21);
		memset(originalFileName,'\0',256);
		memset(fileMetaBuffer,'\0',8192);
		isOrigin = false;
	}

};


extern map<int,searchResponseInfo> indexSearchResponseMap;
extern int getFileNumber;
extern char getFileName[512];

extern int searchIndex;

extern unsigned char charHexLookupConverter(unsigned char ch);
extern char delNonce[41];
extern char delSHA1[41];
extern char delFileName[512];
extern void writeIndexStructuresToFile();
extern void retrieveIndexStructure();
